import fs from "fs/promises";
import path from "path";
import { randomUUID, createHash } from "crypto";
import { FileModel, FileCreate, File } from "server/models/File";

// 文件上传配置
export interface UploadConfig {
  maxFileSize: number; // 最大文件大小（字节）
  allowedMimeTypes: string[]; // 允许的MIME类型
  storageType: "local" | "minio" | "qiniu"; // 存储类型
  basePath: string; // 基础存储路径
}

// 上传结果
export interface UploadResult {
  success: boolean;
  file?: File;
  error?: string;
  url?: string;
}

// 云存储配置
export interface CloudStorageConfig {
  minio?: {
    endpoint: string;
    port: number;
    accessKey: string;
    secretKey: string;
    bucketName: string;
    useSSL: boolean;
  };
  qiniu?: {
    accessKey: string;
    secretKey: string;
    bucketName: string;
    domain: string;
  };
}

export class FileUploadService {
  private config: UploadConfig;
  private cloudConfig: CloudStorageConfig;

  constructor(config: UploadConfig, cloudConfig: CloudStorageConfig = {}) {
    this.config = config;
    this.cloudConfig = cloudConfig;
  }

  /**
   * 生成唯一的文件名
   */
  private generateFileName(originalName: string): string {
    const ext = path.extname(originalName);
    const name = path.basename(originalName, ext);
    const timestamp = Date.now();
    const random = randomUUID().slice(0, 8);
    return `${name}_${timestamp}_${random}${ext}`;
  }

  /**
   * 验证文件
   */
  private validateFile(file: {
    size: number;
    mimetype: string;
  }): string | null {
    if (file.size > this.config.maxFileSize) {
      return `文件大小不能超过 ${this.formatFileSize(this.config.maxFileSize)}`;
    }

    if (!this.config.allowedMimeTypes.includes(file.mimetype)) {
      return `不支持的文件类型: ${file.mimetype}`;
    }

    return null;
  }

  /**
   * 格式化文件大小
   */
  private formatFileSize(bytes: number): string {
    const units = ["B", "KB", "MB", "GB"];
    let size = bytes;
    let unitIndex = 0;

    while (size >= 1024 && unitIndex < units.length - 1) {
      size /= 1024;
      unitIndex++;
    }

    return `${size.toFixed(1)} ${units[unitIndex]}`;
  }

  /**
   * 本地存储上传
   */
  private async uploadToLocal(
    fileBuffer: Buffer,
    fileName: string,
    category: string
  ): Promise<{ filePath: string; url: string }> {
    const categoryPath = path.join(this.config.basePath, category);
    const filePath = path.join(categoryPath, fileName);

    // 确保目录存在
    await fs.mkdir(categoryPath, { recursive: true });

    // 写入文件
    await fs.writeFile(filePath, fileBuffer);

    // 生成访问URL（相对路径）
    const relativePath = path.relative(this.config.basePath, filePath);
    const url = `/uploads/${relativePath.replace(/\\/g, "/")}`;

    return { filePath: relativePath, url };
  }

  /**
   * MinIO上传（需要安装minio包）
   */
  private async uploadToMinIO(
    fileBuffer: Buffer,
    fileName: string,
    category: string
  ): Promise<{ filePath: string; url: string }> {
    try {
      // 使用动态导入替代 require
      const { Client } = await import("minio");

      // 解析endpoint，分离主机和端口
      let endpoint = this.cloudConfig.minio!.endpoint;
      let port = this.cloudConfig.minio!.port;

      const minioClient = new Client({
        endPoint: endpoint,
        port: port,
        useSSL: this.cloudConfig.minio!.useSSL,
        accessKey: this.cloudConfig.minio!.accessKey,
        secretKey: this.cloudConfig.minio!.secretKey,
      });

      const bucketName = this.cloudConfig.minio!.bucketName;
      const objectName = `${category}/${fileName}`;

      await minioClient.putObject(bucketName, objectName, fileBuffer);

      // 如果分类是public，使用普通URL，不需要签名
      if (category === "public") {
        const minioProtocol = this.cloudConfig.minio!.useSSL ? "https" : "http";
        // 如果端口是80(http)或443(https)，则在URL中省略端口号
        const portStr =
          (this.cloudConfig.minio!.useSSL && port === 443) ||
          (!this.cloudConfig.minio!.useSSL && port === 80)
            ? ""
            : `:${port}`;
        const url = `${minioProtocol}://${endpoint}${portStr}/${bucketName}/${objectName}`;
        return { filePath: objectName, url };
      }

      // 生成带签名的URL，有效期为7天
      const url = await minioClient.presignedGetObject(
        bucketName,
        objectName,
        7 * 24 * 60 * 60
      );

      return { filePath: objectName, url };
    } catch (error) {
      console.error("MinIO 上传失败:", error);
      throw new Error(
        "MinIO 上传失败: " +
          (error instanceof Error ? error.message : "未知错误")
      );
    }
  }

  /**
   * 七牛云上传（需要安装qiniu包）
   */
  private async uploadToQiniu(
    fileBuffer: Buffer,
    fileName: string,
    category: string
  ): Promise<{ filePath: string; url: string }> {
    // 这里需要安装 qiniu 包
    // const qiniu = require('qiniu')

    // const mac = new qiniu.auth.digest.Mac(
    //   this.cloudConfig.qiniu!.accessKey,
    //   this.cloudConfig.qiniu!.secretKey
    // )

    // const config = new qiniu.conf.Config()
    // const bucketManager = new qiniu.rs.BucketManager(mac, config)

    // const key = `${category}/${fileName}`
    // const putPolicy = new qiniu.rs.PutPolicy({
    //   scope: `${this.cloudConfig.qiniu!.bucketName}:${key}`
    // })

    // const uploadToken = putPolicy.uploadToken(mac)
    // const formUploader = new qiniu.form_up.FormUploader(config)
    // const putExtra = new qiniu.form_up.PutExtra()

    // return new Promise((resolve, reject) => {
    //   formUploader.put(uploadToken, key, fileBuffer, putExtra, (err, body, info) => {
    //     if (err) reject(err)
    //     if (info.statusCode === 200) {
    //       const url = `${this.cloudConfig.qiniu!.domain}/${key}`
    //       resolve({ filePath: key, url })
    //     } else {
    //       reject(new Error(`上传失败: ${info.statusCode}`))
    //     }
    //   })
    // })

    throw new Error("七牛云上传功能需要安装qiniu包并配置七牛云服务");
  }

  /**
   * 计算文件哈希值
   */
  private calculateFileHash(buffer: Buffer): string {
    return createHash("sha256").update(buffer).digest("hex");
  }

  /**
   * 检查重复文件
   */
  private async checkDuplicateFile(
    fileHash: string,
    category: string
  ): Promise<File | null> {
    // 首先根据哈希值查找完全重复的文件
    const duplicateFile = await FileModel.findByFileHash(fileHash, category);
    if (duplicateFile) {
      return duplicateFile;
    }

    return null;
  }

  /**
   * 上传文件
   */
  async uploadFile(
    file: {
      buffer: Buffer;
      originalname: string;
      mimetype: string;
      size: number;
    },
    category: string,
    userId?: number
  ): Promise<UploadResult> {
    try {
      // 验证文件
      const validationError = this.validateFile(file);
      if (validationError) {
        return { success: false, error: validationError };
      }

      // 计算文件哈希值
      const fileHash = this.calculateFileHash(file.buffer);

      // 检查重复文件
      const duplicateFile = await this.checkDuplicateFile(fileHash, category);
      if (duplicateFile) {
        // 返回已存在的文件信息，不重复上传
        const existingFileUrl = await this.generateFileUrl(duplicateFile);
        return {
          success: true,
          file: duplicateFile,
          url: existingFileUrl,
        };
      }

      // 生成文件名
      const fileName = this.generateFileName(file.originalname);

      let uploadResult: { filePath: string; url: string };

      // 根据存储类型选择上传方式
      switch (this.config.storageType) {
        case "local":
          uploadResult = await this.uploadToLocal(
            file.buffer,
            fileName,
            category
          );
          break;
        case "minio":
          if (!this.cloudConfig.minio) {
            return { success: false, error: "MinIO配置未设置" };
          }
          uploadResult = await this.uploadToMinIO(
            file.buffer,
            fileName,
            category
          );
          break;
        case "qiniu":
          if (!this.cloudConfig.qiniu) {
            return { success: false, error: "七牛云配置未设置" };
          }
          uploadResult = await this.uploadToQiniu(
            file.buffer,
            fileName,
            category
          );
          break;
        default:
          return { success: false, error: "不支持的存储类型" };
      }

      // 保存文件记录到数据库
      const fileRecord: FileCreate = {
        original_name: file.originalname,
        file_name: fileName,
        file_path: uploadResult.filePath,
        file_size: file.size,
        mime_type: file.mimetype,
        storage_type: this.config.storageType,
        category: category,
        bucket_name:
          this.config.storageType !== "local"
            ? this.config.storageType === "minio"
              ? this.cloudConfig.minio?.bucketName
              : this.cloudConfig.qiniu?.bucketName
            : undefined,
        file_hash: fileHash,
        created_by: userId,
      };

      const createdFile = await FileModel.create(fileRecord);

      return {
        success: true,
        file: createdFile,
        url: uploadResult.url,
      };
    } catch (error) {
      console.error("文件上传失败:", error);
      return {
        success: false,
        error: error instanceof Error ? error.message : "上传失败",
      };
    }
  }

  /**
   * 删除文件
   */
  async deleteFile(
    fileId: number
  ): Promise<{ success: boolean; error?: string }> {
    try {
      const file = await FileModel.findById(fileId);
      if (!file) {
        return { success: false, error: "文件不存在" };
      }

      // 根据存储类型删除物理文件
      switch (file.storage_type) {
        case "local":
          const fullPath = path.join(this.config.basePath, file.file_path);
          await fs.unlink(fullPath).catch(() => {
            // 文件可能不存在，继续执行数据库删除
          });
          break;
        case "minio":
          // 实现MinIO删除逻辑
          break;
        case "qiniu":
          // 实现七牛云删除逻辑
          break;
      }

      // 软删除数据库记录
      await FileModel.softDelete(fileId);

      return { success: true };
    } catch (error) {
      console.error("文件删除失败:", error);
      return {
        success: false,
        error: error instanceof Error ? error.message : "删除失败",
      };
    }
  }

  /**
   * 获取文件列表
   */
  async getFiles(category?: string, page: number = 1, limit: number = 20) {
    const offset = (page - 1) * limit;

    if (category) {
      return await FileModel.findByCategory(category, limit, offset);
    } else {
      return await FileModel.findAll(limit, offset);
    }
  }

  /**
   * 获取文件统计信息
   */
  async getStats() {
    return await FileModel.getStats();
  }

  /**
   * 根据文件记录生成完整的访问URL
   */
  async generateFileUrl(file: File): Promise<string> {
    if (!file) return "";

    switch (file.storage_type) {
      case "local":
        // 本地存储：使用相对路径
        return `/uploads/${file.file_path.replace(/\\\\/g, "/")}`;

      case "minio":
        if (!this.cloudConfig.minio) return "";

        // 如果分类是public，使用普通URL，不需要签名
        if (file.category === "public") {
          const minioProtocol = this.cloudConfig.minio.useSSL
            ? "https"
            : "http";
          // 如果端口是80(http)或443(https)，则在URL中省略端口号
          const port = this.cloudConfig.minio.port;
          const portStr =
            (this.cloudConfig.minio.useSSL && port === 443) ||
            (!this.cloudConfig.minio.useSSL && port === 80)
              ? ""
              : `:${port}`;
          return `${minioProtocol}://${this.cloudConfig.minio.endpoint}${portStr}/${this.cloudConfig.minio.bucketName}/${file.file_path}`;
        }

        try {
          // 使用动态导入替代 require
          const { Client } = await import("minio");

          const minioClient = new Client({
            endPoint: this.cloudConfig.minio.endpoint,
            port: this.cloudConfig.minio.port,
            useSSL: this.cloudConfig.minio.useSSL,
            accessKey: this.cloudConfig.minio.accessKey,
            secretKey: this.cloudConfig.minio.secretKey,
          });

          // 生成带签名的URL，有效期为1小时
          return await minioClient.presignedGetObject(
            this.cloudConfig.minio.bucketName,
            file.file_path,
            60 * 60
          );
        } catch (error) {
          console.error("生成MinIO签名URL失败:", error);
          // 降级到普通URL
          const minioProtocol = this.cloudConfig.minio.useSSL
            ? "https"
            : "http";
          // 如果端口是80(http)或443(https)，则在URL中省略端口号
          const port = this.cloudConfig.minio.port;
          const portStr =
            (this.cloudConfig.minio.useSSL && port === 443) ||
            (!this.cloudConfig.minio.useSSL && port === 80)
              ? ""
              : `:${port}`;
          return `${minioProtocol}://${this.cloudConfig.minio.endpoint}${portStr}/${this.cloudConfig.minio.bucketName}/${file.file_path}`;
        }

      case "qiniu":
        if (!this.cloudConfig.qiniu) return "";
        // 七牛云存储：使用配置的域名
        return `${this.cloudConfig.qiniu.domain}/${file.file_path}`;

      default:
        return "";
    }
  }

  /**
   * 根据文件ID生成完整的访问URL
   */
  async generateFileUrlById(fileId: number) {
    try {
      const file = await FileModel.findById(fileId);
      if (!file) return "";

      return await this.generateFileUrl(file);
    } catch (error) {
      console.error("获取文件URL失败:", error);
      return "";
    }
  }
}

// 从环境变量获取配置
function getUploadConfig(): UploadConfig {
  const fileUploadConfig = useRuntimeConfig().fileUpload;
  const maxFileSize = fileUploadConfig.MAX_FILE_SIZE
    ? parseFileSize(fileUploadConfig.MAX_FILE_SIZE)
    : 10 * 1024 * 1024; // 默认10MB

  const storageType =
    (fileUploadConfig.STORAGE_TYPE as "local" | "minio" | "qiniu") || "local";
  const basePath = fileUploadConfig.UPLOAD_PATH || "./uploads";

  return {
    maxFileSize,
    allowedMimeTypes: [
      "image/jpeg",
      "image/png",
      "image/gif",
      "image/webp",
      "image/svg+xml",
      "application/pdf",
      "application/msword",
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "application/vnd.ms-excel",
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "text/plain",
      "application/zip",
      "application/x-rar-compressed",
    ],
    storageType,
    basePath,
  };
}

// 解析文件大小字符串（如 10MB, 1GB）
function parseFileSize(sizeStr: string): number {
  const units: { [key: string]: number } = {
    B: 1,
    KB: 1024,
    MB: 1024 * 1024,
    GB: 1024 * 1024 * 1024,
  };

  const match = sizeStr.match(/^(\d+)([BKMG]B?)$/i);
  if (!match) return 10 * 1024 * 1024; // 默认10MB

  const value = parseInt(match[1]);
  const unit = match[2].toUpperCase();

  return value * (units[unit] || units["MB"]);
}

// 从环境变量获取云存储配置
function getCloudStorageConfig(): CloudStorageConfig {
  const runtimeConfig = useRuntimeConfig();
  const storageType = runtimeConfig.fileUpload.STORAGE_TYPE || "local";

  if (storageType === "minio") {
    return {
      minio: {
        endpoint: runtimeConfig.minio.MINIO_ENDPOINT || "",
        port: Number(runtimeConfig.minio.MINIO_PORT) || 9000,
        accessKey: runtimeConfig.minio.MINIO_ACCESS_KEY || "",
        secretKey: runtimeConfig.minio.MINIO_SECRET_KEY || "",
        bucketName: runtimeConfig.minio.MINIO_BUCKET_NAME || "uploads",
        useSSL: runtimeConfig.minio.MINIO_USE_SSL === "true",
      },
    };
  }

  if (storageType === "qiniu") {
    return {
      qiniu: {
        accessKey: runtimeConfig.qiniu.QINIU_ACCESS_KEY || "",
        secretKey: runtimeConfig.qiniu.QINIU_SECRET_KEY || "",
        bucketName: runtimeConfig.qiniu.QINIU_BUCKET_NAME || "uploads",
        domain: runtimeConfig.qiniu.QINIU_DOMAIN || "",
      },
    };
  }

  return {};
}

// 创建基于环境变量的文件上传服务实例
export const fileUploadService = new FileUploadService(
  getUploadConfig(),
  getCloudStorageConfig()
);
